package woaini.wind.bot.chat;

import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.collection.BoundedPriorityQueue;
import cn.hutool.core.collection.CollUtil;
import lombok.Getter;
import woaini.wind.bot.chat.config.ChatterBaseConfig;
import woaini.wind.bot.chat.dto.ChatMessage;
import woaini.wind.bot.chat.stream.WindChatStream;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.SubmissionPublisher;

/**
 * ai聊天顶层抽象
 *
 * @see AbstractChatter
 * @author yefeng {@date 2024-09-03 17:36:35}
 */
@Getter
public abstract class AbstractChatter {

  protected ChatterBaseConfig chatterBaseConfig;

  protected TimedCache<String, BoundedPriorityQueue<ChatMessage>> chatHistory;

  public AbstractChatter(ChatterBaseConfig chatterBaseConfig) {
    this.chatterBaseConfig = chatterBaseConfig;
    chatHistory = new TimedCache<>(1000 * 60 * 3);
  }

  public void streamChat(
      WindChatStream windChatStream, String userId, String userName, String text) {

    // 消息发布
    SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
    publisher.subscribe(windChatStream);

    // 获取缓存队列
    BoundedPriorityQueue<ChatMessage> cacheMessageQueue = chatHistory.get(userId);
    ChatMessage chatMessage = ChatMessage.of(text);
    Boolean memory = chatterBaseConfig.getMemory();

    // 获取历史消息
    List<ChatMessage> messages =
      this.historicalMessage(chatMessage, cacheMessageQueue, memory, userId);

    this.streamHandlerChat(cacheMessageQueue,publisher, userId, userName, messages);
  }

  /**
   * @param userId 用户id
   * @param userName 用户名称
   * @param text 聊天的话
   * @author yefeng {@date 2024-09-03 17:48:06}
   * @since 1.0
   * @return {@link String }
   */
  public String chat(String userId, String userName, String text) {

    // 获取缓存队列
    BoundedPriorityQueue<ChatMessage> cacheMessageQueue = chatHistory.get(userId);
    ChatMessage chatMessage = ChatMessage.of(text);
    Boolean memory = chatterBaseConfig.getMemory();

    // 获取历史消息
    List<ChatMessage> messages =
        this.historicalMessage(chatMessage, cacheMessageQueue, memory, userId);

    // 调用会话
    String result = this.handlerChat(userId, userName, messages);
    if (memory) {
      cacheMessageQueue.add(ChatMessage.ofAssistant(result));
    }
    return result;
  }

  private List<ChatMessage> historicalMessage(
      ChatMessage chatMessage,
      BoundedPriorityQueue<ChatMessage> cacheMessageQueue,
      Boolean memory,
      String userId) {

    if (memory) {
      if (cacheMessageQueue == null) {
        cacheMessageQueue =
            new BoundedPriorityQueue<>(20, Comparator.comparing(ChatMessage::getCreateTime));
        chatHistory.put(userId, cacheMessageQueue);
      }
      // 用户说的话加入缓存
      cacheMessageQueue.add(chatMessage);
    }
    // 消息合并
    // 系统设定填入
    List<String> prompts = chatterBaseConfig.getPrompts();
    List<ChatMessage> systemMessage = prompts.stream().map(ChatMessage::ofSystem).toList();
    List<ChatMessage> messages = new ArrayList<>(systemMessage);
    // 取出缓存记录
    if (memory) {
      List<ChatMessage> cacheMessages = new ArrayList<>(cacheMessageQueue.stream().toList());
      // 排序处理
      CollUtil.sort(cacheMessages, Comparator.comparing(ChatMessage::getCreateTime));
      messages.addAll(cacheMessages);
    } else {
      messages.add(chatMessage);
    }
    return messages;
  }

  /**
   * @param userId 用户id
   * @param messages 消息列表
   * @author yefeng {@date 2024-09-03 18:19:59}
   * @since 1.0
   * @return {@link String }
   */
  public abstract String handlerChat(String userId, String userName, List<ChatMessage> messages);

  /**
   * 流式聊天
   *
   * @param userId 用户 ID
   * @param userName 用户名
   * @param messages 消息
   * @author yefeng {@date 2025-06-22 09:33:07}
   * @since 1.0
   * @return {@link WindChatStream }
   */
  public abstract void streamHandlerChat(
    BoundedPriorityQueue<ChatMessage> cacheMessageQueue,
      SubmissionPublisher<String> publisher,
      String userId,
      String userName,
      List<ChatMessage> messages);

  /**
   * 使用情况查询
   *
   * @return
   */
  public abstract String useInfo();
}
